/*
 * ml20dll
 * This source code is under the GNU Public License (GPL).
 * Written by Ramiro Polla
 * Initial release: july 14th, 2005
 *
 * this dll reads ml20 files
 */

#include "ml20dll.h"

imgret_t imgret;

void dump_frame( unsigned int filepos, vstream_t *vstream, ML20F *ml20f )
{
    udps *tmp_u = (udps *) filepos;
    vcs *tmp_v = (vcs *) filepos;
    int i;
    int frame_tot;
    uint8_t *tdata;
    FILE *in = vstream->file;

    if( IS_SET( vstream->flags, VSTREAM_UDP ) )
        fseek( in, tmp_u->filepos, SEEK_SET );
    else if( IS_SET( vstream->flags, VSTREAM_VC ) )
        fseek( in, tmp_v->filepos, SEEK_SET );
    else
        fseek( in, filepos, SEEK_SET );

    if( IS_SET( vstream->flags, VSTREAM_VC ) ) {
        //TODO make a fake ml20h
        ml20f->h.size = 0;
        ml20f->h.height = 240; //TODO hardcoded
        ml20f->h.width = 320; //TODO hardcoded
        ml20f->h.ff = !tmp_v->vc_hdr.nkeyframe;
        ml20f->h.time = tmp_v->vc_hdr.timestamp;
        ml20f->data = NULL;
    } else {
        fread( &ml20f->h, sizeof(ML20H), 1, in );
        ml20f->data = malloc( ml20f->h.size );
    }

    if( IS_SET( vstream->flags, VSTREAM_UDP ) ) {
        tdata = ml20f->data;
        fread( tdata, tmp_u->size - sizeof(ML20H), 1, in );
        tdata += tmp_u->size - sizeof(ML20H);

        frame_tot = tmp_u->udp_hdr.frame_tot;

        tmp_u = tmp_u->next;

        for( i = 1 ; i < frame_tot && tmp_u ; i++, tmp_u = tmp_u->next ) {
            fseek( in, tmp_u->filepos, SEEK_SET );
            fread( tdata, tmp_u->size, 1, in );
            tdata += tmp_u->size;
        }
    } else if( IS_SET( vstream->flags, VSTREAM_VC ) ) {
        tdata = 0;
        frame_tot = tmp_v->vc_hdr.frame_tot;
        ml20f->h.size = 0;

        {
            vcs *ttmp_v = tmp_v;
            for( i = 0 ; i < frame_tot && ttmp_v ; i++, ttmp_v = ttmp_v->next )
                ml20f->h.size += ttmp_v->vc_hdr.size;

            ml20f->data = malloc( ml20f->h.size );
            tdata = ml20f->data;
        }

        for( i = 0 ; i < frame_tot && tmp_v ; i++, tmp_v = tmp_v->next ) {
            fseek( in, tmp_v->filepos, SEEK_SET );
            fread( tdata, tmp_v->vc_hdr.size, 1, in );

            tdata += tmp_v->vc_hdr.size;
        }
    }
    else {
        fread( ml20f->data, ml20f->h.size, 1, in );
    }
}

int read_next_keyframe( vstream_t *vstream, ML20F *ml20f, int flags, udps *frame )
{
    if( IS_SET( vstream->flags, VSTREAM_UDP ) )
        return(int) udp_next_keyframe( frame, vstream->file, ml20f );
    else if( IS_SET( vstream->flags, VSTREAM_VC ) )
        return(int) vc_next_keyframe( (vcs*) frame, vstream->file, ml20f );
    else
        return ml20_next_keyframe( vstream->file, ml20f, flags );
}

// flags:
// READ_DATA
// READ_SEEK_IN
// READ_SEEK_OUT
// READ_SEEK_FIRST
// READ_RET_IN
// return: -1 on error, 0 on success, frame start if READ_RET_IN
int read_next_frame( vstream_t *vstream, ML20F *ml20f, int flags, udps *frame )
{
    if( IS_SET( vstream->flags, VSTREAM_UDP ) )
        return(int) udp_next_frame( frame, vstream->file, ml20f );
    else if( IS_SET( vstream->flags, VSTREAM_VC ) )
        return(int) vc_next_frame( (vcs*) frame, vstream->file, ml20f );
    else
        return ml20_next_frame( vstream->file, ml20f, flags );
}

imgret_p ml20_first_keyframe( char *fullname, msn_t *msnt )
{
    unsigned int pos = -1;
    udps *first_u = 0;
    udps *last_u = 0;
    udps *new_u = 0;

    vcs *first_v = 0;
    vcs *last_v = 0;
    vcs *new_v = 0;

    ML20F ml20f;
    vstream_t *vstream = calloc( 1, sizeof(vstream_t) );
    decoder_t *decoder;

    vstream->file = fopen( fullname, "rb" );
    if( !vstream->file ) {
        free( vstream );
        return 0;
    }

    vstream->fullname = malloc( strlen( fullname ) + 1 );
    strcpy( vstream->fullname, fullname );

    {
        char *ext;
        for( ext = fullname + strlen(fullname); ext != fullname ; ext-- )
            if( *ext == '.' )
                break;
        if( !stricmp( ext, ".udp" ) ) {
            SET_BIT( vstream->flags, VSTREAM_UDP );
            first_u = read_udps( vstream->file, 0 );
            last_u = first_u;
            if( first_u ) {
                for( new_u = read_udps( vstream->file, first_u->filepos+first_u->size ) ; new_u ; new_u = read_udps( vstream->file, new_u->filepos+new_u->size ) ) {
                    fit_udps_part( &first_u, &last_u, new_u );
                    if( (pos = (unsigned int) udp_next_keyframe( first_u, vstream->file, &ml20f )) != -1 )
                        break;
                }
            }
        } else if( !stricmp( ext, ".vc" ) ) {
            SET_BIT( vstream->flags, VSTREAM_VC );
            first_v = read_vcs( vstream->file, 0 );
            last_v = first_v;
            if( first_v )
                for( new_v = read_vcs( vstream->file, first_v->filepos+first_v->vc_hdr.size ) ; new_v ; new_v = read_vcs( vstream->file, new_v->filepos+new_v->vc_hdr.size ) ) {
                    fit_vcs_part( &first_v, &last_v, new_v );
                    if( (pos = (unsigned int) vc_next_keyframe( first_v, vstream->file, &ml20f )) != -1 )
                        break;
                }
        }
        else
            pos = ml20_next_keyframe( vstream->file, &ml20f, READ_DATA | READ_RET_IN );
    }

    if( pos == -1 ) {
        fclose( vstream->file );
        return 0;
    }

    vstream_add_frame( vstream, pos, &ml20f );

    if( IS_SET( vstream->flags, VSTREAM_VC ) ) {
        vstream->height = 240;
        vstream->width = 320;
    } else {
        vstream->height = ml20f.h.height;
        vstream->width = ml20f.h.width;
    }

    decoder = new_decoder( vstream, msnt );
    if( !decoder ) {
        kill_vstream( vstream );
        return 0;
    }

    decode_frame( decoder, vstream->ff );

    imgret.height = vstream->height;
    imgret.width = vstream->width;
    memcpy( imgret.imagebuffer, decoder->kol, decoder->BITM.biSizeImage );

    kill_decoder( decoder );
    kill_vstream( vstream );

    return &imgret;
}

int ml20_next_keyframe( FILE *inf, ML20F *ml20f, int flags )
{
    int firstpos, lastpos;

    if( !inf )
        return -1;

    firstpos = ftell( inf );

    do {
        if( (lastpos = ml20_next_frame( inf, ml20f, READ_SEEK_OUT | READ_RET_IN )) == -1 )
            return -1;
    }
    while( !IS_SET( ml20f->h.ff, 1 ) );

    if( IS_SET( flags, READ_DATA ) ) {
        fseek( inf, lastpos, SEEK_SET );
        if( ml20_next_frame( inf, ml20f, READ_DATA ) == -1 )
            return -1;
    }

    if( IS_SET( flags, READ_SEEK_IN ) )
        fseek( inf, lastpos, SEEK_SET );
    else if( IS_SET( flags, READ_SEEK_FIRST ) )
        fseek( inf, firstpos, SEEK_SET );

    if( IS_SET( flags, READ_RET_IN ) )
        return lastpos;

    return 0;
}

int ml20_next_frame( FILE *inf, ML20F *ml20f, int flags )
{
    int firstpos;
    int nextpos;

    if( !inf )
        return -1;

    firstpos = ftell( inf );
    nextpos = firstpos-1;

    do {
        nextpos++;
        fseek( inf, nextpos, SEEK_SET );
        if( !fread( &ml20f->h, sizeof(ML20H), 1, inf ) )
            return -1;
    }
    while( ml20f->h.signature != FOURCC_ML20 );

    { // Correctly set the keyframe bit.
        ml20_int_h temp;
        int position = ftell( inf );
        fread( &temp, 1, sizeof(temp), inf );
        if( !IS_SET( temp.nkf, 1 ) )
            SET_BIT( ml20f->h.ff, 1 );
        fseek( inf, position, SEEK_SET );
    }

    if( IS_SET( flags, READ_DATA ) ) {
        ml20f->data = malloc( ml20f->h.size );
        if( !fread( ml20f->data, ml20f->h.size, 1, inf ) ) {
            free( ml20f->data );
            return -1;
        }
    }
    else if( IS_SET( flags, READ_SEEK_OUT ) )
        fseek( inf, ml20f->h.size, SEEK_CUR );

    if( IS_SET( flags, READ_SEEK_IN ) )
        fseek( inf, nextpos, SEEK_SET );
    else if( IS_SET( flags, READ_SEEK_FIRST ) )
        fseek( inf, firstpos, SEEK_SET );

    if( IS_SET( flags, READ_RET_IN ) )
        return nextpos;

    return 0;
}

char *ml20dll_version( )
{
    return VERSION_STRING;
}
